'use client';

import dagre from '@dagrejs/dagre';
import {
  Background,
  ConnectionLineType,
  ReactFlow,
  addEdge,
  useEdgesState,
  useNodesState,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { observer } from 'mobx-react-lite';
import { useCallback, useEffect, useState } from 'react';

import SpinnerSVG from '@/components/svg/SpinnerSVG';
import { useApiClient } from '@/lib/apiClient';
import { getClipTitle } from '@/utils/clip';

import { HistoryNode } from './HistoryNode';

const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));

const nodeWidth = 172;
const nodeHeight = 36;

const nodeTypes = {
  default: HistoryNode,
};

const HistoryClient = observer<any>(({ id }: { id: string }) => {
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const apiClient = useApiClient();
  const [nodes, setNodes, onNodesChange] = useNodesState([] as any[]);
  const [edges, setEdges, onEdgesChange] = useEdgesState([] as any[]);

  const getLayoutedElements = (nodes?: any[], edges?: any[]) => {
    dagreGraph.setGraph({ rankdir: 'TB' });

    (nodes || []).forEach((node: any) => {
      dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
    });

    (edges || []).forEach((edge: any) => {
      dagreGraph.setEdge(edge.source, edge.target);
    });

    dagre.layout(dagreGraph);

    const newNodes = (nodes || []).map((node: any) => {
      const nodeWithPosition = dagreGraph.node(node.id);
      const newNode = {
        ...node,
        targetPosition: 'top',
        sourcePosition: 'bottom',
        // We are shifting the dagre node position (anchor=center center) to the top left
        // so it matches the React Flow node anchor point (top left).
        position: {
          x: nodeWithPosition.x - nodeWidth / 2,
          y: nodeWithPosition.y - nodeHeight / 2,
        },
      };

      return newNode;
    });

    return { nodes: newNodes, edges };
  };

  useEffect(() => {
    const loadData = async () => {
      setIsLoading(true);
      const { data } = await apiClient.GET('/api/clips/children', {
        params: {
          query: {
            clip_id: id,
            descend_from_roots: false,
            follow_concat_paths: true,
          },
        },
      });
      setIsLoading(false);

      const position = { x: 0, y: 0 };
      const edgeType = 'simplebezier';
      const nodesMap: any = {};

      [...(data?.root_clips || []), ...(data?.child_clips || [])].forEach(
        (childClip: any) => {
          nodesMap[childClip.clip?.id || childClip.id] = {
            id: childClip.clip?.id || childClip.id,
            type: 'default',
            data: {
              label: getClipTitle(childClip.clip || childClip),
              id: childClip.clip?.id || childClip.id,
            },
            style: { color: 'black', fontWeight: 'bold' },
            sourcePosition: 'bottom',
            targetPosition: 'top',
            position,
          };
        }
      );

      const dataNodes = Object.values(nodesMap);

      const dataEdges = data?.child_clips
        .map((childClip: any) => {
          return childClip.parent_ids.map((parentId: string) => ({
            id: `${parentId}_${childClip.clip.id}`,
            source: parentId,
            target: childClip.clip.id,
            type: edgeType,
            animated: true,
          }));
        })
        .flat();

      const { nodes: layoutedNodes, edges: layoutedEdges } =
        getLayoutedElements(dataNodes, dataEdges);

      setNodes(layoutedNodes);
      setTimeout(() => {
        setEdges(layoutedEdges || []);
      }, 0);
    };

    loadData();
  }, [id]);

  const onConnect = useCallback(
    (params: any) =>
      setEdges((eds) => addEdge({ ...params, animated: true }, eds || [])),
    []
  );

  return isLoading ? (
    <div className='flex h-full w-full items-center justify-center'>
      <SpinnerSVG />
    </div>
  ) : (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      nodeTypes={nodeTypes}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
      connectionLineType={ConnectionLineType.SimpleBezier}
      fitView
      style={{ backgroundColor: '#0e0808' }}
    >
      <Background />
    </ReactFlow>
  );
});

export default HistoryClient;
